home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
language
/
pcl_src.zoo
/
lap.lsp
< prev
next >
Wrap
Lisp/Scheme
|
1992-07-09
|
17KB
|
502 lines
;;;-*-Mode: LISP; Package: PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
;;; All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted. Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;;
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;;
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;; CommonLoops Coordinator
;;; Xerox PARC
;;; 3333 Coyote Hill Rd.
;;; Palo Alto, CA 94304
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
(in-package 'pcl)
;;;
;;; This file defines PCL's interface to the LAP mechanism.
;;;
;;; The file is divided into two parts. The first part defines the interface
;;; used by PCL to create abstract LAP code vectors. PCL never creates lists
;;; that represent LAP code directly, it always calls this mechanism to do so.
;;; This provides a layer of error checking on the LAP code before it gets to
;;; the implementation-specific assembler. Note that this error checking is
;;; syntactic only, but even so is useful to have. Because of it, no specific
;;; LAP assembler should worry itself with checking the syntax of the LAP code.
;;;
;;; The second part of the file defines the LAP assemblers for each PCL port.
;;; These are included together in the same file to make it easier to change
;;; them all should some random change be made in the LAP mechanism.
;;;
(defvar *make-lap-closure-generator*)
(defvar *precompile-lap-closure-generator*)
(defvar *lap-in-lisp*)
(defun make-lap-closure-generator
(closure-variables arguments iregs vregs fvregs tregs lap-code)
(funcall-function *make-lap-closure-generator*
closure-variables arguments iregs
vregs fvregs tregs lap-code))
(defmacro precompile-lap-closure-generator
(cvars args i-regs v-regs fv-regs t-regs lap)
(funcall-function *precompile-lap-closure-generator*
cvars args i-regs
v-regs fv-regs t-regs lap))
(defmacro lap-in-lisp (cvars args iregs vregs fvregs tregs lap)
(declare (ignore cvars args))
`(locally (declare #.*optimize-speed*)
,(make-lap-prog iregs vregs fvregs tregs
(flatten-lap lap (opcode :label 'exit-lap-in-lisp)))))
;;;
;;; The following functions and macros are used by PCL when generating LAP
;;; code:
;;;
;;; GENERATING-LAP
;;; WITH-LAP-REGISTERS
;;; ALLOCATE-REGISTER
;;; DEALLOCATE-REGISTER
;;; LAP-FLATTEN
;;; OPCODE
;;; OPERAND
;;;
(proclaim '(special *generating-lap*)) ;CAR - alist of free registers
;CADR - alist of allocated registers
;CADDR - max reg number allocated
;
;in each alist, the entries have
;the form: (type . (:REG <n>))
;
;;;
;;; This goes around the generation of any lap code. <body> should return a lap
;;; code sequence, this macro will take care of converting that to a lap closure
;;; generator.
;;;
(defmacro generating-lap (closure-variables arguments &body body)
`(let* ((*generating-lap* (list () () -1)))
(finalize-lap-generation nil ,closure-variables ,arguments (progn ,@body))))
(defmacro generating-lap-in-lisp (closure-variables arguments &body body)
`(let* ((*generating-lap* (list () () -1)))
(finalize-lap-generation t ,closure-variables ,arguments (progn ,@body))))
;;;
;;; Each register specification looks like:
;;;
;;; (<var> <type> &key :reuse <other-reg>)
;;;
(defmacro with-lap-registers (register-specifications &body body)
;;
;; Given that, for now, there is only one keyword argument and
;; that, for now, we do no error checking, we can be pretty
;; sleazy about how this works.
;;
(flet ((make-allocations ()
(gathering1 (collecting)
(dolist (spec register-specifications)
(gather1
`(,(car spec) (or ,(cadddr spec) (allocate-register ',(cadr spec))))))))
(make-deallocations ()
(gathering1 (collecting)
(dolist (spec register-specifications)
(gather1
`(unless ,(cadddr spec) (deallocate-register ,(car spec))))))))
`(let ,(make-allocations)
(multiple-value-prog1 (progn ,@body)
,@(make-deallocations)))))
(defun allocate-register (type)
(destructuring-bind (free allocated) *generating-lap*
(let ((entry (assoc type free)))
(cond (entry
(setf (car *generating-lap*) (delete entry free)
(cadr *generating-lap*) (cons entry allocated))
(cdr entry))
(t
(let ((new `(,type . (:reg ,(incf (the fixnum (caddr *generating-lap*)))))))
(setf (cadr *generating-lap*) (cons new allocated))
(cdr new)))))))
(defun deallocate-register (reg)
(let ((entry (rassoc reg (cadr *generating-lap*))))
(unless entry (error "Attempt to free an unallocated register."))
(push entry (car *generating-lap*))
(setf (cadr *generating-lap*) (delete entry (cadr *generating-lap*)))))
(defvar *precompiling-lap* nil)
(defun finalize-lap-generation (in-lisp-p closure-variables arguments lap-code)
(when (cadr *generating-lap*) (error "Registers still allocated when lap being finalized."))
(let ((iregs ())
(vregs ())
(fvregs ())
(tregs ()))
(dolist (entry (car *generating-lap*))
(ecase (car entry)
(index (push (caddr entry) iregs))
(vector (push (caddr entry) vregs))
(fixnum-vector (push (caddr entry) fvregs))
((t) (push (caddr entry) tregs))))
(cond (in-lisp-p
`(lap-in-lisp ,closure-variables ,arguments ,iregs
,vregs ,fvregs ,tregs ,lap-code))
(*precompiling-lap*
(values closure-variables arguments iregs
vregs fvregs tregs lap-code))
(t
(make-lap-closure-generator
closure-variables arguments iregs
vregs fvregs tregs lap-code)))))
(defun flatten-lap (&rest opcodes-or-sequences)
(let ((result ()))
(dolist (opcode-or-sequence opcodes-or-sequences result)
(cond ((null opcode-or-sequence))
((not (consp (car opcode-or-sequence))) ;its an opcode
(setf result (append result (list opcode-or-sequence))))
(t
(setf result (append result opcode-or-sequence)))))))
(defmacro flattening-lap ()
'(let ((result ()))
(values #'(lambda (value) (push value result))
#'(lambda () (apply #'flatten-lap (reverse result))))))
;;;
;;; This code deals with the syntax of the individual opcodes and operands.
;;;
;;;
;;; The first two of these variables are documented to all ports. They are
;;; lists of the symbols which name the lap opcodes and operands. They can
;;; be useful to determine whether a port has implemented all the required
;;; opcodes and operands.
;;;
;;; The third of these variables is for use of the emitter only.
;;;
(defvar *lap-operands* ())
(defvar *lap-opcodes* ())
(defvar *lap-emitters* (make-hash-table :test #'eq :size 30))
(defun opcode (name &rest args)
(let ((emitter (gethash name *lap-emitters*)))
(if emitter
(apply-function (symbol-function emitter) args)
(error "No opcode named ~S." name))))
(defun operand (name &rest args)
(let ((emitter (gethash name *lap-emitters*)))
(if emitter
(apply-function (symbol-function emitter) args)
(error "No operand named ~S." name))))
(defmacro defopcode (name types)
(let ((fn-name (symbol-append "LAP Opcode " name *the-pcl-package*))
(lambda-list
(mapcar #'(lambda (x) (declare (ignore x)) (gensym)) types)))
`(progn
(eval-when (l